package im.vinci.server.feed.service;

import im.vinci.server.common.domain.music.MusicSong;
import im.vinci.server.feed.domain.Feed;
import im.vinci.server.feed.domain.FeedType;
import im.vinci.server.feed.domain.UserPlayTimeInfo;
import im.vinci.server.feed.domain.pagecontent.HealthyMotionContent;
import im.vinci.server.feed.domain.pagecontent.MotionPlay;
import im.vinci.server.feed.domain.pagecontent.MusicFeedContent;
import im.vinci.server.feed.domain.pagecontent.VinciPlayListContent;
import im.vinci.server.feed.domain.wrapper.UserPlayInfoDAO;
import im.vinci.server.feed.persistence.FeedMapper;
import im.vinci.server.feed.persistence.UserPlayInfoMapper;
import im.vinci.server.feed.persistence.UserPlayTimeInfoMapper;
import im.vinci.server.syncdb.domain.content.HeartRate;
import im.vinci.server.syncdb.domain.content.Step;
import im.vinci.server.syncdb.persistence.UserDataSyncMapper;
import im.vinci.server.user.persistence.RealUserAndLoginMapper;
import im.vinci.server.user.persistence.UserAttentionMapper;
import im.vinci.server.utils.DateUtils;
import im.vinci.server.utils.JsonUtils;
import im.vinci.server.utils.LocalIdGenerator;
import im.vinci.server.utils.VinciAccountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * Created by mingjie on 17/2/24.
 */
@Component
public class ScheduledTask {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RealUserAndLoginMapper userAndLoginMapper;

    @Autowired
    private UserAttentionMapper userAttentionMapper;

    @Autowired
    private UserPlayInfoMapper userPlayInfoMapper;

    @Autowired
    private UserPlayTimeInfoMapper userPlayTimeInfoMapper;

    @Autowired
    private FeedMapper feedMapper;

    @Autowired
    private UserDataSyncMapper userDataSyncMapper;

    @Autowired
    private Environment env;

    private boolean isGenerateVPlayList;

    private boolean isGenerateVHealthyMotion;

    @PostConstruct
    public void init() {
        String str = env.getProperty("isGenerateVPlayList", "FALSE");
        isGenerateVPlayList = Boolean.parseBoolean(str);

        str = env.getProperty("isGenerateVHealthyMotion", "FALSE");
        isGenerateVHealthyMotion = Boolean.parseBoolean(str);

        logger.info("Read config, isGenerateVPlayList:{}, isGenerateVHealthyMotion:{}", isGenerateVPlayList, isGenerateVHealthyMotion);
    }

    /**
     * 定时任务
     * 每天22点获取用户及其好友当天的播放音乐的数据, 解析生成播放排行
     */
    @Scheduled(cron = "0 0 22 * * ?")
    public void scheduledTask() {
        if (!isGenerateVPlayList) {
            return;
        }

        logger.info("## start to generate VINCI_TODAY_PLAY_LIST feeds !!!");

        long startTime = System.currentTimeMillis();

        // 获取时间 当日22点
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        Date startDate = calendar.getTime();

        calendar.set(Calendar.HOUR_OF_DAY, 22);
        Date endDate = calendar.getTime();

        List<Long> userIdList = userAndLoginMapper.selectUserIdList();

        for (Long userId : userIdList) {
            try {
                List<Long> friendIdList = userAttentionMapper.selectFriendList(userId);

                friendIdList.add(userId);

                // 从当日播放信息中统计播放时长
                List<UserPlayInfoDAO> userPlayInfoDAOList = userPlayInfoMapper.batchSelect(friendIdList, startDate, endDate);

                HashMap<Long, UserPlayTimeInfo> map = new HashMap<>();
                for (UserPlayInfoDAO userPlayInfoDAO : userPlayInfoDAOList) {
                    UserPlayTimeInfo userPlayTimeInfo = map.get(userPlayInfoDAO.getUserId());
                    if (userPlayTimeInfo == null) {
                        userPlayTimeInfo = new UserPlayTimeInfo();
                        userPlayTimeInfo.setUserId(userPlayInfoDAO.getUserId());
                        userPlayTimeInfo.setCreateTime(endDate);

                        map.put(userPlayInfoDAO.getUserId(), userPlayTimeInfo);
                    }

                    MusicSong musicSong = JsonUtils.decode(userPlayInfoDAO.getMusicInfo(), MusicSong.class);
                    int timeInSeconds = userPlayTimeInfo.getDurationTime() + musicSong.getPlaySeconds();
                    userPlayTimeInfo.setDurationTime(timeInSeconds);
                }

                List<UserPlayTimeInfo> list = new ArrayList<>(map.size());
                for (Map.Entry<Long, UserPlayTimeInfo> entry : map.entrySet()) {
                    UserPlayTimeInfo userPlayTimeInfo = entry.getValue();

                    if (userPlayTimeInfo.getDurationTime() <= 0 && userPlayTimeInfo.getUserId() != userId) {
                        continue;
                    }

                    int timeInMinutes = userPlayTimeInfo.getDurationTime() % 60 == 0 ? userPlayTimeInfo.getDurationTime() / 60 : (userPlayTimeInfo.getDurationTime() / 60) + 1;
                    userPlayTimeInfo.setDurationTime(timeInMinutes);
                    list.add(userPlayTimeInfo);
                }

                if (!map.containsKey(userId)) {
                    UserPlayTimeInfo userPlayTimeInfo = new UserPlayTimeInfo();
                    userPlayTimeInfo.setUserId(userId);
                    userPlayTimeInfo.setDurationTime(0);
                    userPlayTimeInfo.setCreateTime(endDate);

                    list.add(userPlayTimeInfo);
                }

                userPlayTimeInfoMapper.batchInsert(userId, list);

                Collections.sort(list, new Comparator<UserPlayTimeInfo>() {
                    @Override
                    public int compare(UserPlayTimeInfo o1, UserPlayTimeInfo o2) {
                        if (o1.getDurationTime() > o2.getDurationTime()) {
                            return -1;
                        } else if (o1.getDurationTime() == o2.getDurationTime()) {
                            return 0;
                        } else {
                            return 1;
                        }
                    }
                });

                VinciPlayListContent vinciPlayListContent = new VinciPlayListContent();
                vinciPlayListContent.setCreateTime(endDate.getTime());

                for (int i = 0; i < list.size(); i++) {
                    UserPlayTimeInfo userPlayTimeInfo = list.get(i);
                    if (i == 0) {
                        vinciPlayListContent.setChampionUserId(userPlayTimeInfo.getUserId());
                    }

                    if (userPlayTimeInfo.getUserId() == userId) {
                        vinciPlayListContent.setPlace(i + 1);
                        vinciPlayListContent.setDurationTime(userPlayTimeInfo.getDurationTime());
                        break;
                    }
                }

                Feed feed = new Feed();
                feed.setUserId(VinciAccountUtil.TODAY_PLAY_LIST_ID + userId);
                feed.setFeedId(LocalIdGenerator.INSTANCE.generateId());
                feed.setPageType(FeedType.VINCI_TODAY_PLAY_LIST.getStr());
                feed.setPageContent(JsonUtils.encode(vinciPlayListContent));
                feed.setTopic("");
                feed.setContent("");
                feed.setDtCreate(endDate);

                feedMapper.publishFeed(feed);
            } catch (Exception e) {
                logger.error("generate VINCI_TODAY_PLAY_LIST feed error, userId:{}", userId, e);
            }
        }

        long endTime = System.currentTimeMillis();

        logger.info("## Finish generating VINCI_TODAY_PLAY_LIST feed, cost time {}", DateUtils.getCostTimeString(startTime, endTime));
    }

    @Scheduled(cron = "0 0 22 * * ?")
    public void scheduledMotionTask() {
        if (!isGenerateVHealthyMotion) {
            return;
        }

        logger.info("## start to generate VINCI_HEALTHY_MOTION feeds !!!");

        long startTime = System.currentTimeMillis();

        // 获取时间 当日22点
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        Date startDateTime = calendar.getTime();

        calendar.set(Calendar.HOUR_OF_DAY, 22);
        Date endDateTime = calendar.getTime();

        List<Long> userIdList = userAndLoginMapper.selectUserIdList();

        for (Long userId : userIdList) {
            try {
                List<UserPlayInfoDAO> playlist = userPlayInfoMapper.select(userId, startDateTime, endDateTime);

                if (playlist == null || playlist.isEmpty()) {
                    continue;
                }

                List<String> heartRateDataList = userDataSyncMapper.getUserTableData(userId, "HEART_RATE", startDateTime, endDateTime);
                if (heartRateDataList == null || heartRateDataList.isEmpty()) {
                    continue;
                }

                List<HeartRate> heartRateList = new ArrayList<>(heartRateDataList.size());
                for (String str : heartRateDataList) {
                    HeartRate heartRate = JsonUtils.decode(str, HeartRate.class);
                    heartRateList.add(heartRate);
                }

                Collections.sort(heartRateList, new Comparator<HeartRate>() {
                    @Override
                    public int compare(HeartRate o1, HeartRate o2) {
                        if (o1.getAvRate() > o2.getAvRate()) {
                            return 1;
                        } else if (o1.getAvRate() == o2.getAvRate()) {
                            return 0;
                        }
                        return -1;
                    }
                });

                HeartRate minRate = heartRateList.get(0);
                HeartRate maxRate = heartRateList.get(heartRateDataList.size() - 1);

                float averageRateData = 0.0f;
                for (HeartRate heartRate : heartRateList) {
                    averageRateData += heartRate.getAvRate();
                }
                averageRateData = averageRateData / heartRateList.size();

                HeartRate closedAverageRate = minRate;
                for (int i = 1; i < heartRateList.size(); i++) {
                    HeartRate temp = heartRateList.get(i);
                    if (Math.abs(closedAverageRate.getAvRate() - averageRateData) > Math.abs(temp.getAvRate() - averageRateData)) {
                        closedAverageRate = temp;
                    } else {
                        break;
                    }
                }
                closedAverageRate.setAvRate(averageRateData);

                List<MotionPlay> motionPlayList = new LinkedList<>();
                motionPlayList.add(generateMotionPlay(playlist, minRate));
                motionPlayList.add(generateMotionPlay(playlist, maxRate));
                motionPlayList.add(generateMotionPlay(playlist, closedAverageRate));

                List<String> stepDataList = userDataSyncMapper.getUserTableData(userId, "STEP", startDateTime, endDateTime);
                int steps = 0;
                for (String str : stepDataList) {
                    Step step = JsonUtils.decode(str, Step.class);
                    steps += step.getSteps();
                }

                HealthyMotionContent healthyMotionContent = new HealthyMotionContent();
                healthyMotionContent.setUserId(userId);
                healthyMotionContent.setHeartRate(averageRateData);
                healthyMotionContent.setSteps(steps);
                healthyMotionContent.setData(motionPlayList);
                healthyMotionContent.setCreateTime(endDateTime.getTime());

                Feed feed = new Feed();
                feed.setUserId(VinciAccountUtil.HEALTHY_MOTION_ID + userId);
                feed.setFeedId(LocalIdGenerator.INSTANCE.generateId());
                feed.setPageType(FeedType.VINCI_HEALTHY_MOTION.getStr());
                feed.setPageContent(JsonUtils.encode(healthyMotionContent));
                feed.setTopic("");
                feed.setContent("");
                feed.setDtCreate(endDateTime);

                feedMapper.publishFeed(feed);


            } catch (Exception e) {
                logger.error("generate VINCI_HEALTHY_MOTION feed error, userId:{}", userId, e);
            }
        }

        long endTime = System.currentTimeMillis();

        logger.info("## Finish generating VINCI_HEALTHY_MOTION feed, cost time {}", DateUtils.getCostTimeString(startTime, endTime));
    }

    private MusicFeedContent findCloseMusic(List<UserPlayInfoDAO> list, long time) {
        UserPlayInfoDAO dao = list.get(0);

        for (int i = 1; i < list.size(); i++) {
            UserPlayInfoDAO temp = list.get(i);
            if (Math.abs(dao.getCreateTime().getTime() - time) > Math.abs(temp.getCreateTime().getTime() - time)) {
                dao = temp;
                continue;
            }
            break;
        }

        MusicFeedContent musicFeedContent = JsonUtils.decode(dao.getMusicInfo(), MusicFeedContent.class);
        return musicFeedContent;
    }

    private MotionPlay generateMotionPlay(List<UserPlayInfoDAO> list, HeartRate heartRate) {
        MotionPlay motionPlay = new MotionPlay();
        MusicFeedContent music = findCloseMusic(list, heartRate.getEnd());
        motionPlay.setHeartRate(heartRate.getAvRate());
        motionPlay.setMusic(music);
        return motionPlay;
    }
}
